import time
import os
from selenium.webdriver import ChromeOptions
from selenium.webdriver import Chrome
# 【2】驱动位置服务
from selenium.webdriver.chrome.service import Service
# 【3】选择器
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from fake_useragent import UserAgent
from copenv_img import CvImageDistance
from urllib import request
import numpy as np
import cv2

# 【二】创建浏览器对象
# 【1】指定驱动位置
executable_path = "./chromedriver.exe"
service = Service(executable_path=executable_path)
options = ChromeOptions()
# 无痕模式
options.add_argument('--incognito')  # 隐身模式（无痕模式）
# 禁用浏览器自动化提示条
options.add_argument('--disable-blink-features=AutomationControlled')
# 排除启用自动化开关
options.add_experimental_option("excludeSwitches", ["enable-automation"])
# 关闭自动化扩展信息
options.add_experimental_option('useAutomationExtension', False)
# 设置Chrome为正常用户模式
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--no-sandbox')

# 设置Agent
options.add_argument(f'--user-agent="{UserAgent().random}"')
# 【2】创建浏览器对象
browser = Chrome(service=service, options=options)
# 设置隐式等待时间为10秒
browser.implicitly_wait(10)
# 关键：执行CDP命令来隐藏webdriver属性，避免被检测
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        })
    """
})

# 京东登录页面
browser.get("https://passport.jd.com/new/login.aspx")

# 用户名输入框
username_input = browser.find_element(By.XPATH, '//*[@id="loginname"]')
password_input = browser.find_element(By.XPATH, '//*[@id="nloginpwd"]')
time.sleep(3)
# 输入用户名密码
username_input.send_keys("18174204151")
password_input.send_keys("dyl20011113...")
time.sleep(1)
# 登录按钮
login_button = browser.find_element(By.XPATH, '//*[@id="loginsubmit"]')
login_button.click()
time.sleep(5)
# 验证码图片
img_element = browser.find_element(By.XPATH, '//*[@id="main_img"]')
tag_element = browser.find_element(By.XPATH, '//*[@id="slot_img"]')

# 获取网页上显示的图片尺寸
web_img_width = img_element.size['width']
web_img_height = img_element.size['height']
web_tag_width = tag_element.size['width']
web_tag_height = tag_element.size['height']
print(f"网页背景图尺寸: {web_img_width}x{web_img_height}")
print(f"网页滑块图尺寸: {web_tag_width}x{web_tag_height}")

# 获取图片链接并保存到本地
tag_img_path = os.path.join(os.path.dirname(__file__), 'tag.png')
main_img_path = os.path.join(os.path.dirname(__file__), 'background.png')
request.urlretrieve(img_element.get_attribute("src"), main_img_path)
request.urlretrieve(tag_element.get_attribute("src"), tag_img_path)
print("图片保存成功")
time.sleep(1)

# 使用 cv2.imdecode 读取图片,解决中文路径问题
def imread_chinese(filepath):
    """读取包含中文路径的图片"""
    img_array = np.fromfile(filepath, dtype=np.uint8)
    img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
    return img

# 读取图片并获取缺口位置
tag_img = imread_chinese(tag_img_path)
main_img = imread_chinese(main_img_path)

# 检查图片是否读取成功
if tag_img is None or main_img is None:
    print("图片读取失败!")
    browser.quit()
    exit()

# 获取本地图片的实际尺寸 (height, width, channels)
local_img_height, local_img_width = main_img.shape[:2]
local_tag_height, local_tag_width = tag_img.shape[:2]
print(f"本地背景图尺寸: {local_img_width}x{local_img_height}")
print(f"本地滑块图尺寸: {local_tag_width}x{local_tag_height}")

# 将图片转为二进制数据
_, tag_img_encoded = cv2.imencode('.png', tag_img)
_, main_img_encoded = cv2.imencode('.png', main_img)
tag_img_data = tag_img_encoded.tobytes()
main_img_data = main_img_encoded.tobytes()

cv_obj = CvImageDistance()
# 使用 from_buffer_get_distanct 方法处理二进制数据
# 注意: 参数顺序是 (滑块图, 背景图)
distance_x, distance_y = cv_obj.from_buffer_get_distanct(tag_img_data, main_img_data)
print(f"图片识别到的缺口位置 X: {distance_x}, Y: {distance_y}")

# 计算缩放比例: 网页显示宽度 / 本地图片实际宽度
scale_ratio = web_img_width / local_img_width
print(f"缩放比例: {scale_ratio:.4f} (网页宽度 {web_img_width} / 本地宽度 {local_img_width})")

# 根据缩放比例调整实际滑动距离
distance = distance_x * scale_ratio
print(f"调整后的滑动距离: {distance:.2f} 像素")
time.sleep(2)
# 根据位置，按住拖动
move_but = browser.find_element(By.XPATH, '//*[@id="captcha_modal"]/div/div[4]/div/img')
actions = ActionChains(browser)
actions.click_and_hold(move_but).perform()
time.sleep(0.2)  # 短暂停顿,模拟人的反应时间

# 模拟人类拖动:先快后慢,有加速和减速过程
track = 0
# 先快速移动到目标位置的80%
target_80 = distance * 0.8
while track < target_80:
    move_distance = min(15, target_80 - track)  # 每次移动最多15像素
    actions.move_by_offset(move_distance, 0).perform()
    track += move_distance
    time.sleep(0.01)  # 快速移动

# 减速移动剩余的20%
while track < distance - 3:  # 留出一点误差空间
    move_distance = min(3, distance - 3 - track)  # 每次移动最多3像素
    actions.move_by_offset(move_distance, 0).perform()
    track += move_distance
    time.sleep(0.05)  # 慢速移动

# 最后微调
final_move = distance - track
if final_move > 0:
    actions.move_by_offset(final_move, 0).perform()
    
time.sleep(0.5)  # 停顿一下再松开
actions.release().perform()
print("滑块拖动完成,等待验证结果...")
time.sleep(5)  # 等待验证结果

